`include "ascon_define.v"

module `ASCON_HASH_CTRL
(
    input                                        ascon_hash_clk_i,
    input                                        ascon_hash_rst_n_i,
//与ctrl regs连接
    input                                        ascon_hash_ctrl_en_i,
    input                      [`CTRL_REG_W-1:0] ascon_hash_ctrl_msg_block_num_i,
    input                      [`CTRL_REG_W-1:0] ascon_hash_ctrl_regs_msg_addr_i,
    input                                        ascon_hash_ctrl_mode_i,
    input                      [`CTRL_REG_W-1:0] ascon_hash_ctrl_h_addr_i,
    output                                       ascon_hash_ctrl_prot_o,
    output                                       ascon_hash_ctrl_fsm_sta_wen_o,
    output                                       ascon_hash_ctrl_fsm_en_wen_o,
    output                     [`CTRL_REG_W-1:0] ascon_hash_ctrl_w_sta_o,
    output                     [`CTRL_REG_W-1:0] ascon_hash_ctrl_w_en_o,
//与ascon_hash_am连接
    input                                        ascon_hash_ctrl_am_vld_i,
    output                                       ascon_hash_ctrl_am_en_o,
    output                                       ascon_hash_ctrl_am_vld_o,
    output                                       ascon_hash_ctrl_am_mode_o,
    output                                       ascon_hash_ctrl_am_first_o,
    output                                       ascon_hash_ctrl_am_last_o,
//与rx buf连接
    // output                                       ascon_hash_ctrl_rx_buf_en_o,
    // output                                       ascon_hash_ctrl_rx_buf_wen_o,
    output                     [`BUF_ADDR_W-1:0] ascon_hash_ctrl_rx_buf_addr_o,
//与tx buf连接
    output                                       ascon_hash_ctrl_tx_buf_en_o,
    output                                       ascon_hash_ctrl_tx_buf_wen_o,
    output                     [`BUF_ADDR_W-1:0] ascon_hash_ctrl_tx_buf_addr_o,
//与ascon_hash_sh连接
    input                                        ascon_hash_ctrl_sh_vld_i,
    output                                       ascon_hash_ctrl_sh_en_o,
    output                                       ascon_hash_ctrl_sh_vld_o,
    output                                       ascon_hash_ctrl_sh_mode_o,
    output                                       ascon_hash_ctrl_sh_first_o
);

localparam IDLE                                  = `ASCON_HASH_CTRL_STA'b0000_0000;
localparam HASH_INIT                             = `ASCON_HASH_CTRL_STA'b0000_0001;
localparam WITE_INIT                             = `ASCON_HASH_CTRL_STA'b0000_0010;
localparam HASH_AM                               = `ASCON_HASH_CTRL_STA'b0000_0100;
localparam WITE_AM                               = `ASCON_HASH_CTRL_STA'b0000_1000;
localparam LAST_AM                               = `ASCON_HASH_CTRL_STA'b0001_0000;
localparam FIRST_SH                              = `ASCON_HASH_CTRL_STA'b0010_0000;
localparam WITE_SH                               = `ASCON_HASH_CTRL_STA'b0100_0000;
localparam HASH_SH                               = `ASCON_HASH_CTRL_STA'b1000_0000;

/********** 接口信号 ***********/
//与ctrl regs连接
wire                                             en_i_p;
wire                           [`CTRL_REG_W-1:0] msg_block_num_p;
wire                           [`CTRL_REG_W-1:0] msg_addr_p;
wire                                             regs_mode_p;
wire                           [`CTRL_REG_W-1:0] h_addr_p;

wire                                             prot_p;
wire                                             fsm_sta_wen_p;
wire                                             fsm_en_wen_p;
wire                           [`CTRL_REG_W-1:0] w_sta_p;
wire                           [`CTRL_REG_W-1:0] w_en_p;
//与ascon_hash_am连接
wire                                             am_vld_i_p;
reg                                              am_en_p;
reg                                              am_vld_o_p;
reg                                              am_mode_p;
reg                                              am_first_p;
reg                                              am_last_p;
//与rx buf连接
// reg                                              rx_buf_en_p;
// reg                                              rx_buf_wen_p;
reg                            [`BUF_ADDR_W-1:0] rx_buf_addr_p;
//与tx buf连接
reg                                              tx_buf_en_p;
reg                                              tx_buf_wen_p;
reg                            [`BUF_ADDR_W-1:0] tx_buf_addr_p;
//与ascon_hash_sh连接
wire                                             sh_vld_i_p;
reg                                              sh_en_p;
reg                                              sh_vld_o_p;
reg                                              sh_mode_p;
reg                                              sh_first_p;

/********** 内部信号 ***********/
//
wire                                             en_i_q;
wire                                             en_i_pos_w;
wire                           [`CTRL_REG_W-1:0] msg_block_num_w;
reg                            [`BUF_ADDR_W-1:0] cur_msg_block_cnt;
reg                                      [3-1:0] hash_block_cnt;

reg                   [`ASCON_HASH_CTRL_STA-1:0] c_state;
reg                   [`ASCON_HASH_CTRL_STA-1:0] n_state;

/********** 连接接口信号 ***********/
//与ctrl regs连接
assign en_i_p                          = ascon_hash_ctrl_en_i;
assign msg_block_num_p                 = ascon_hash_ctrl_msg_block_num_i;
assign msg_addr_p                      = ascon_hash_ctrl_regs_msg_addr_i;
assign regs_mode_p                     = ascon_hash_ctrl_mode_i;
assign h_addr_p                        = ascon_hash_ctrl_h_addr_i;
assign ascon_hash_ctrl_prot_o          = prot_p;
assign ascon_hash_ctrl_fsm_sta_wen_o   = fsm_sta_wen_p;
assign ascon_hash_ctrl_fsm_en_wen_o    = fsm_en_wen_p;
assign ascon_hash_ctrl_w_sta_o         = w_sta_p;
assign ascon_hash_ctrl_w_en_o          = w_en_p;
//与ascon_hash_am连接
assign am_vld_i_p                      = ascon_hash_ctrl_am_vld_i;
assign ascon_hash_ctrl_am_en_o         = am_en_p;
assign ascon_hash_ctrl_am_vld_o        = am_vld_o_p;
assign ascon_hash_ctrl_am_mode_o       = am_mode_p;
assign ascon_hash_ctrl_am_first_o      = am_first_p;
assign ascon_hash_ctrl_am_last_o       = am_last_p;
//与rx buf连接
// assign ascon_hash_ctrl_rx_buf_en_o     = rx_buf_en_p;
// assign ascon_hash_ctrl_rx_buf_wen_o    = rx_buf_wen_p;
assign ascon_hash_ctrl_rx_buf_addr_o   = rx_buf_addr_p;
//与tx buf连接
assign ascon_hash_ctrl_tx_buf_en_o     = tx_buf_en_p;
assign ascon_hash_ctrl_tx_buf_wen_o    = tx_buf_wen_p;
assign ascon_hash_ctrl_tx_buf_addr_o   = tx_buf_addr_p;
//与ascon_hash_sh连接
assign sh_vld_i_p                      = ascon_hash_ctrl_sh_vld_i;
assign ascon_hash_ctrl_sh_en_o         = sh_en_p;
assign ascon_hash_ctrl_sh_vld_o        = sh_vld_o_p;
assign ascon_hash_ctrl_sh_mode_o       = sh_mode_p;
assign ascon_hash_ctrl_sh_first_o      = sh_first_p;

/********** 内部信号生成 ***********/

assign msg_block_num_w  = msg_block_num_p>>3;

assign prot_p           = (c_state != IDLE) ? 1'b1 : 1'b0;
assign fsm_sta_wen_p    = en_i_p;
assign fsm_en_wen_p     = ((c_state == WITE_SH)&&(n_state == IDLE)) ? 1'b1 : 1'b0;
assign w_sta_p          = ((c_state == WITE_SH)&&(n_state == IDLE)) ? `CTRL_REG_W'h8000_0000 : n_state;
assign w_en_p           = ((c_state == WITE_SH)&&(n_state == IDLE)) ? `CTRL_REG_W'h0 : `CTRL_REG_W'h1;
assign en_i_pos_w       = en_i_p & (~en_i_q);                                  //使能上升沿信号

`ANY_CYC
u_en_i_p_1_cyc
     (
     .clk_i                            (ascon_hash_clk_i                       ),
     .rst_n_i                          (ascon_hash_rst_n_i                     ),
     .en_i                             (1'b1                                   ),
     .dat_d_i                          (en_i_p                                 ),
     .dat_q_o                          (en_i_q                                 )
     );

//由状态机控制的内部控制信号
always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : CUR_MSG_BLOCK_CNT_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    cur_msg_block_cnt   <= `BUF_ADDR_W'b0;
  else if (c_state == WITE_SH && n_state == IDLE)
    cur_msg_block_cnt   <= `BUF_ADDR_W'b0;
  else if (en_i_pos_w == 1'b1)
    cur_msg_block_cnt   <= msg_block_num_w[`BUF_ADDR_W-1:0];
  else if (((c_state == WITE_INIT) && (n_state == HASH_AM))||
           ((c_state == WITE_AM) && (n_state == HASH_AM)))
    cur_msg_block_cnt   <= cur_msg_block_cnt - `BUF_ADDR_W'b1;
  else
    cur_msg_block_cnt   <= cur_msg_block_cnt;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : HASH_BLOCK_CNT_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    hash_block_cnt      <= 3'b0;
  else if (c_state == WITE_SH && n_state == IDLE)
    hash_block_cnt      <= 3'b0;
  else if (en_i_pos_w == 1'b1)
    hash_block_cnt      <= 3'd4;
  else if (((c_state == LAST_AM) && (n_state == FIRST_SH))||
           ((c_state == WITE_SH) && (n_state == HASH_SH)))
    hash_block_cnt      <= hash_block_cnt - 3'b1;
  else
    hash_block_cnt      <= hash_block_cnt;
end
/********** 由状态机控制的输出信号 ***********/
//与ascon_hash_am连接
always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : AM_EN_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    am_en_p             <= 1'b0;
  else if ((c_state == IDLE && n_state == HASH_INIT)||
           (c_state == IDLE && n_state == LAST_AM))  //当只有一个msg block时,不经过p计算
    am_en_p             <= 1'b1;
  else if (c_state == LAST_AM && n_state == FIRST_SH)
    am_en_p             <= 1'b0;
  else
    am_en_p             <= am_en_p;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : AM_VLD_O_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    am_vld_o_p          <= 1'b0;
  else if ((c_state == IDLE && n_state == HASH_INIT)||
           (c_state == IDLE && n_state == LAST_AM)|| //当只有一个msg block时,不经过p计算
           (c_state == WITE_INIT && n_state == HASH_AM)||
           (c_state == WITE_AM && n_state == HASH_AM))
    am_vld_o_p          <= 1'b1;
  else if ((c_state == HASH_INIT && n_state == WITE_INIT)||
           (c_state == LAST_AM && n_state == FIRST_SH)|| //当只有一个msg block时,不经过p计算
           (c_state == HASH_AM && n_state == WITE_AM))
    am_vld_o_p          <= 1'b0;
  else
    am_vld_o_p          <= am_vld_o_p;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : AM_MODE_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    am_mode_p           <= 1'b0;
  else if ((c_state == IDLE && n_state == HASH_INIT)||
           (c_state == IDLE && n_state == LAST_AM)) //当只有一个msg block时,不经过p计算
    am_mode_p           <= regs_mode_p;
  else if (c_state == WITE_INIT && n_state == HASH_AM)
    am_mode_p           <= regs_mode_p;
  else
    am_mode_p           <= am_mode_p;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : AM_FIRST_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    am_first_p          <= 1'b0;
  else if ((c_state == IDLE && n_state == HASH_INIT)||
           (c_state == IDLE && n_state == LAST_AM)) //当只有一个msg block时,不经过p计算
    am_first_p          <= 1'b1;
  else if ((c_state == HASH_INIT && n_state == WITE_INIT)||
           (c_state == LAST_AM && n_state == FIRST_SH)) //当只有一个msg block时,不经过p计算
    am_first_p          <= 1'b0;
  else
    am_first_p          <= am_first_p;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : AM_LAST_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    am_last_p           <= 1'b0;
  else if ((c_state == WITE_INIT && n_state == LAST_AM)||
           (c_state == IDLE && n_state == LAST_AM)||//当只有一个msg block时,不经过p计算
           (c_state == WITE_AM && n_state == LAST_AM))
    am_last_p           <= 1'b1;
  else if (c_state == LAST_AM && n_state == FIRST_SH)
    am_last_p           <= 1'b0;
  else
    am_last_p           <= am_last_p;
end
//与rx buf连接
// always @(*)
// begin : RX_BUF_EN_P_PROG
//   if ((c_state == WITE_INIT && n_state == HASH_AM)||
//       (c_state == WITE_AM && n_state == HASH_AM))
//     rx_buf_en_p         = 1'b1;
//   else
//     rx_buf_en_p         = 1'b0;
// end

// always @(*)
// begin : RX_BUF_WEN_P_PROG
//   rx_buf_wen_p          = 1'b0;
// end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : RX_BUF_ADDR_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    rx_buf_addr_p       <= `BUF_ADDR_W'b0;
  else if (c_state == WITE_SH && n_state == IDLE)
    rx_buf_addr_p       <= `BUF_ADDR_W'b0;
  else if ((c_state == IDLE && n_state == LAST_AM)||
           (c_state == IDLE && n_state == HASH_INIT))
    rx_buf_addr_p       <= msg_addr_p>>3;
  else if ((c_state == HASH_INIT && n_state == WITE_INIT)||
           (c_state == WITE_INIT && n_state == HASH_AM)||
           (c_state == WITE_AM && n_state == HASH_AM))
    rx_buf_addr_p       <= rx_buf_addr_p + `BUF_ADDR_W'b1;
  else
    rx_buf_addr_p       <= rx_buf_addr_p;
end
//与tx buf连接
always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : TX_BUF_EN_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    tx_buf_en_p         <= 1'b0;
  else if ((c_state == WITE_SH && n_state == HASH_SH)||
           (c_state == WITE_SH && n_state == IDLE))
    tx_buf_en_p         <= 1'b1;
  else
    tx_buf_en_p         <= 1'b0;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : TX_BUF_WEN_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    tx_buf_wen_p        <= 1'b0;
  else if ((c_state == WITE_SH && n_state == HASH_SH)||
           (c_state == WITE_SH && n_state == IDLE))
    tx_buf_wen_p        <= 1'b1;
  else
    tx_buf_wen_p        <= 1'b0;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : TX_BUF_ADDR_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    tx_buf_addr_p       <= `BUF_ADDR_W'b0;
  // else if (c_state == WITE_SH && n_state == IDLE)
  //   tx_buf_addr_p       <= `BUF_ADDR_W'b0;
  else if (c_state == LAST_AM && n_state == FIRST_SH)
    tx_buf_addr_p       <= h_addr_p>>3;
  else if (c_state == HASH_SH && n_state == WITE_SH)
    tx_buf_addr_p       <= tx_buf_addr_p + `BUF_ADDR_W'b1;
  else
    tx_buf_addr_p       <= tx_buf_addr_p;
end

//与ascon_hash_sh连接
always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : SH_EN_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    sh_en_p             <= 1'b0;
  // else if (c_state == LAST_AM && n_state == FIRST_SH)
  else if ((c_state == IDLE && n_state == LAST_AM)||//当只有一个msg block时,不经过p计算
           (c_state == WITE_AM && n_state == LAST_AM)||
           (c_state == WITE_INIT && n_state == LAST_AM))
    sh_en_p             <= 1'b1;
  else if (c_state == WITE_SH && n_state == IDLE)
    sh_en_p             <= 1'b0;
  else
    sh_en_p             <= sh_en_p;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : SH_VLD_O_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    sh_vld_o_p          <= 1'b0;
  // else if ((c_state == LAST_AM && n_state == FIRST_SH)||
  else if ((c_state == IDLE && n_state == LAST_AM)||//当只有一个msg block时,不经过p计算
           (c_state == WITE_AM && n_state == LAST_AM)||
           (c_state == WITE_INIT && n_state == LAST_AM)||
           (c_state == WITE_SH && n_state == HASH_SH))
    sh_vld_o_p          <= 1'b1;
  // else if ((c_state == FIRST_SH && n_state == WITE_SH)||
  else if ((c_state == LAST_AM && n_state == FIRST_SH)||
           (c_state == HASH_SH && n_state == WITE_SH))
    sh_vld_o_p          <= 1'b0;
  else
    sh_vld_o_p          <= sh_vld_o_p;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : SH_MODE_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    sh_mode_p           <= 1'b0;
  // else if (c_state == LAST_AM && n_state == FIRST_SH)
  else if ((c_state == IDLE && n_state == LAST_AM)||//当只有一个msg block时,不经过p计算
           (c_state == WITE_AM && n_state == LAST_AM)||
           (c_state == WITE_INIT && n_state == LAST_AM))
    sh_mode_p           <= 1'b1;
  else if (c_state == WITE_SH && n_state == HASH_SH)
    sh_mode_p           <= regs_mode_p;
  else
    sh_mode_p           <= sh_mode_p;
end

always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : SH_FIRST_P_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    sh_first_p          <= 1'b0;
  // else if (c_state == LAST_AM && n_state == FIRST_SH)
  else if ((c_state == IDLE && n_state == LAST_AM)||//当只有一个msg block时,不经过p计算
           (c_state == WITE_AM && n_state == LAST_AM)||
           (c_state == WITE_INIT && n_state == LAST_AM))
    sh_first_p          <= 1'b1;
  else
    sh_first_p          <= 1'b0;
end

/********** 状态机 ***********/
always @(posedge ascon_hash_clk_i or negedge ascon_hash_rst_n_i)
begin : C_STATE_PROG
  if (ascon_hash_rst_n_i == 1'b0)
    c_state             <= `ASCON_HASH_CTRL_STA'b0;
  else
    c_state             <= n_state;
end


//状态转换
always @(*)
begin : N_STATE_PROG
  case (c_state)
    IDLE      :
      if (en_i_pos_w == 1'b1)
        if (msg_block_num_w == `BUF_ADDR_W'd1)
          n_state       = LAST_AM;
        else
          n_state       = HASH_INIT;
      else
        n_state         = IDLE;
    HASH_INIT :
      n_state           = WITE_INIT;
    WITE_INIT :
      if (am_vld_i_p == 1'b1)
        if (cur_msg_block_cnt == `BUF_ADDR_W'd2)
          n_state       = LAST_AM;
        else
          n_state       = HASH_AM;
      else
        n_state         = WITE_INIT;
    HASH_AM   :
      n_state           = WITE_AM;
    WITE_AM   :
      if (am_vld_i_p == 1'b1)
        if (cur_msg_block_cnt == `BUF_ADDR_W'd2)
          n_state       = LAST_AM;
        else if (cur_msg_block_cnt > `BUF_ADDR_W'd0)
          n_state       = HASH_AM;
        else
          n_state       = WITE_AM;
      else
        n_state         = WITE_AM;
    LAST_AM   :
      n_state           = FIRST_SH;
    FIRST_SH  :
      n_state           = WITE_SH;
    WITE_SH   :
      if (sh_vld_i_p == 1'b1)
        if (hash_block_cnt == 3'd0)
          n_state       = IDLE;
        else if (hash_block_cnt > 3'd0)
          n_state       = HASH_SH;
        else
          n_state       = WITE_SH;
      else
        n_state         = WITE_SH;
    HASH_SH:
      n_state           = WITE_SH;
    default:
      n_state           = IDLE;
  endcase
end


endmodule
